﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using System.Threading;

using System.Collections.Generic;

namespace FlockTest
{
    public class Flock
    {
        public List<FlockElement> flock = new List<FlockElement>();
        private Timer timer;
        private static Vector flockToPoint;



        public Flock(int Population)
        {
            populateFlock(Population);
            setFlockToPoint();
            initFlockingLoop();
        }


        // Populate Flock
        private void populateFlock(int Population)
        {
            for (int i = 0; i < Population; i++) flock.Add(new FlockElement());
            flock[0].IsLeader = true;
        }

        // Initalise Flock Counter
        private void initFlockingLoop()
        {
            TimerCallback callback = new TimerCallback(this.flockingLoop);
            timer = new Timer(flockingLoop, null, 0, 25);
        }

        private void flockingLoop(Object State)
        {
            foreach (FlockElement e1 in flock)
            {
                e1.Pull = Vector.Zero;

                foreach (FlockElement e2 in flock)
                {
                    UpdatePull(e1, e2);
                }

                UpdateCurrentPosition(e1);
            }
        }


        public static void UpdatePull(FlockElement e1, FlockElement e2)
        {
            Vector direction = e1.Coordinates - e2.Coordinates;
            float distance = Vector.Magnitude(direction) ;

            //System.Diagnostics.Debug.WriteLine("direction:{0},distance:{1}", direction.ToString(), distance);

            if (distance == 0F)
            {
                // Do Nothing
            }
            else if (distance > e1.ArBoundary && e1.IsLeader != true)
            {
                // Move towards (+=) at a constant Attraction (direction/distance =  1)
                if (e2.IsLeader)
                {
                    e1.Pull -= (direction / distance) * Global.ATTRACTION_TO_LEADER ;
                }
                else
                {
                    e1.Pull -= (direction / distance) * e1.Attraction;
                }
                
            }
            else
            {
                //Move Away
                e1.Pull += (direction / distance) * e1.Repulsion;
            }

            if(e1.IsLeader) 
            {
                Vector directionFP = e1.Coordinates - flockToPoint;
                float distanceFP = Vector.Magnitude(directionFP);
                e1.Pull -= (directionFP / distanceFP) * Global.ATTRACTION_TO_FPOINT;

                if (distanceFP < Global.FPOINT_RESET_DISTANCE) setFlockToPoint();//;
            }

        }

        public static void UpdateCurrentPosition(FlockElement fe)
        {
            // Get seconds from Last Tick, then tick
            float time = fe.DeltaTime(); fe.Tick();

            Vector direction;

            if (Vector.Magnitude(fe.Pull) > 0) direction = fe.Pull / Vector.Magnitude(fe.Pull);
            else direction = Vector.Zero;

            float acceleration = fe.MaxPull;


            // new Velocity
            fe.Velocity = fe.Velocity + direction * time * acceleration;

            
            

            // Check that the flock hasnt reached maximum velocity
            if (Vector.Magnitude(fe.Velocity) > fe.MaxVelocity) fe.Velocity = direction * fe.MaxVelocity;
            if (Vector.Magnitude(fe.Velocity) < Global.MIN_VELOCITY) fe.Velocity = direction * Global.MIN_VELOCITY;


            // Update position for this tick
            fe.Coordinates += fe.Velocity;
            
            


            // Bounce back from border
            if (fe.Coordinates.X < 0) { fe.Velocity.X = -fe.Velocity.X; fe.Coordinates.X = -fe.Coordinates.X; }
            if (fe.Coordinates.X > Global.BOUNDING_X) { fe.Velocity.X = -fe.Velocity.X; fe.Coordinates.X = 2 * Global.BOUNDING_X - fe.Coordinates.X;  }

            if (fe.Coordinates.Y < 0) { fe.Velocity.Y = -fe.Velocity.Y; fe.Coordinates.Y = -fe.Coordinates.Y; }
            if (fe.Coordinates.Y > Global.BOUNDING_Y) { fe.Velocity.Y = -fe.Velocity.Y; fe.Coordinates.Y = 2 * Global.BOUNDING_Y - fe.Coordinates.Y; }

            if (fe.Coordinates.Z < 0) { fe.Velocity.Z = -fe.Velocity.Z; fe.Coordinates.Z = -fe.Coordinates.Z; }
            if (fe.Coordinates.Z > Global.BOUNDING_Z) { fe.Velocity.Z = -fe.Velocity.Z; fe.Coordinates.Z = 2 * Global.BOUNDING_Z - fe.Coordinates.Z; }

        }

        static void setFlockToPoint()
        {
            flockToPoint = new Vector(FlockMath.Rbd() * Global.BOUNDING_X,
                              FlockMath.Rbd() * Global.BOUNDING_Y,
                              FlockMath.Rbd() * Global.BOUNDING_Z);
        }

        public static Vector FlockToPoint
        {
            get { return flockToPoint; }
            set { flockToPoint = value; }
        }
    }
}
